﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;
using System.Security.Cryptography;
using MUD.Lib.Utils;
using MUD.Lib.Objects.Users;

namespace MUD.Lib.Daemons
{
    /// <summary>
    /// Handles the details of logging in and out. Yes, I'm playing enums-as-state-machines here. Leave me alone.
    /// </summary>
    internal class Login : InputTrap<Login.LoginState>
    {
        public enum LoginState
        {
            Name,
            NameExistsPass,
            NameConfirm,
            NewPassword1,
            NewPassword2,
            Email,
            CharacterSequence
        }

        private int InvalidLoginTries = 0;

        public Login(User user)
            :base (user)
        {
            InitializeSteps();
        }

        private void InitializeSteps()
        {
            AddStage(LoginState.Name, new InputHandler(ProcessName));
            AddStage(LoginState.NameExistsPass, new InputHandler(ValidatePassword));
            AddStage(LoginState.NameConfirm, new InputHandler(NameConfirm));
            AddStage(LoginState.NewPassword1, new InputHandler(NewPassword1));
            AddStage(LoginState.NewPassword2, new InputHandler(NewPassword2));
            AddStage(LoginState.Email, new InputHandler(EmailHandler));
            AddStage(LoginState.CharacterSequence, new InputHandler(ToCharacterSequence));
        }

        // First thing we're going to see from the user.
        private bool ProcessName(User user, string input)
        {
            if (!string.IsNullOrEmpty(input))
            {
                user.Username = input;

                // attempt to load the user;
                user.Load();
            }
            else
            {
                user.Write("Please enter a name:");
                return false;
            }

            if (!string.IsNullOrEmpty(user.Password))
            {
                // ok, there is a user by this name.
                _state = LoginState.NameExistsPass;
                user.Write("Welcome back, " + user.Username + "! Please enter your password:\r\n");
            }
            else
            {
                // this is a new user.
                _state = LoginState.NameConfirm;
                user.Write("Are you sure this is the account name you wish to use? (y/n)\r\n");
            }
            return true;
        }

        private bool ValidatePassword(User user, string input)
        {
            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
            if (user.Password == BitConverter.ToString(md5.ComputeHash(ASCIIEncoding.ASCII.GetBytes(input))))
            {
                // password valid, continue to character selection
                // TODO: Move to character creation/selection
                _state = LoginState.CharacterSequence;
                ToCharacterSequence(user, "y");
            }
            else
            {
                // password invalid, ask them to try again.
                InvalidLoginTries++;
                if (InvalidLoginTries < 3)
                {
                    user.Write("Password invalid, please try again:\r\n ");
                    return false;
                }
                else
                {
                    user.Write("Maximum login attempts reached, disconnecting.\r\n");
                    user.OnExclusiveReceive -= Listener;
                    user.Disconnect();
                }
            }
            return true;
        }

        private bool NameConfirm(User user, string input)
        {
            if (input.ToLower() == "y")
            {
                // they want this name, move on.
                user.Write("Great! Please enter a password.\r\n");
                _state = LoginState.NewPassword1;
            }
            else
            {
                user.Write("Ok, what name would you like to use?\r\n");
                _state = LoginState.Name;
            }
            return true;
        }

        private bool NewPassword1(User user, string input)
        {
            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
            user.Password = BitConverter.ToString(md5.ComputeHash(ASCIIEncoding.ASCII.GetBytes(input)));
            _state = LoginState.NewPassword2;
            user.Write("Please reenter your password:\r\n");
            return true;
        }

        private bool NewPassword2(User user, string input)
        {
            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
            if (BitConverter.ToString(md5.ComputeHash(ASCIIEncoding.ASCII.GetBytes(input))) == user.Password)
            {
                // Password accepted, move on.
                _state = LoginState.Email;
                user.Write("Please enter a valid email address.\r\n");
            }
            else
            {
                // Passwords don't match, reenter.
                _state = LoginState.NewPassword1;
                user.Write("Passwords do not match, please choose a password:\r\n");
            }
            return true;
        }

        private bool EmailHandler(User user, string input)
        {
            if (IsEmailValid(input))
            {
                // store it and move on to character selection/creation!
                user.Email = input;
                user.Write("Ok, your new account with username " + user.Username + " and email address: " + user.Email + " is created. Please verify these details before continuing. (y/n)\r\n Warning: entering 'n' will cause the creation process to restart!\r\n");
                _state = LoginState.CharacterSequence;
            }
            else
            {
                // oops, need to reenter.
                user.Write("Email invalid, please enter a valid email address.");
            }
            return true;
        }

        private bool ToCharacterSequence(User user, string input)
        {
            if (input.ToLower() == "y")
            {
                // TODO: load character select/creation sequence
                user.OnExclusiveReceive -= Listener;
                user.Save();
                user.Write("Welcome to MUD.Net!!\r\n");
                // HACK: This is to test container logic and room movement.
                user.Move(user.MasterObj.StartRoom);
            }
            else
            {
                user.Write("Ok, starting over. Please enter desired username.\r\n");
                _state = LoginState.Name;
            }
            return true;
        }

        private bool IsEmailValid(string email)
        {
            if (string.IsNullOrEmpty(email))
                return false;
            // is there more than one "@"?
            int First = email.IndexOf('@');
            int Last = email.IndexOf('@');
            if (First != Last && First < email.Length - 1)
                return false;
            return true;
        }        

    }
}
